<template>
  <div>
    <!-- 三级联动选择框组件 向子组件传递数据和绑定自定义事件(实现组件间通信) -->
    <Category @send-id="getID" :scene="scene"></Category>
    <!-- 属性列表 -->
    <el-card shadow="hover">
      <!-- 展示属性列表结构 -->
      <div v-show="scene === 0">
        <!-- 添加按钮 -->
        <el-button
          :disabled="!attrList.length"
          type="primary"
          icon="Plus"
          @click="addAttr"
          v-has="`btn.Attr.add`"
        >
          添加属性
        </el-button>
        <!-- 属性列表 height属性可以实现固定表头 stripe表格斑马纹 border表格边框  data表格要展示的数据-->
        <el-table :data="attrList" stripe style="width: 100%; margin-top: 10px">
          <!-- 自定义空数据时的显示内容 -->
          <template #empty>
            <img
              style="width: 280px"
              src="@/assets/images/empty.png"
              alt="没有数据"
            />
          </template>
          <!-- 序号列 prop属性值(data中的属性) type类型(index为自动生成的序号) align对齐方式-->
          <el-table-column
            label="序号"
            type="index"
            width="80"
            align="center"
          />
          <!-- 属性名称列 -->
          <el-table-column
            prop="attrName"
            label="属性名称"
            width="120"
            align="left"
          />
          <!-- 属性值名称列 -->
          <el-table-column label="属性值名称" align="left">
            <!-- 通过el-table-column默认插槽回传的数据，获取到表格当前行的数据进行渲染 -->
            <template #default="{ row }">
              <!-- 标签 key唯一值  -->
              <el-tag
                v-for="item in row.attrValueList"
                :key="item.id"
                effect="light"
                style="margin: 5px"
                type="primary"
              >
                {{ item.valueName }}
              </el-tag>
            </template>
          </el-table-column>
          <!-- 操作列 -->
          <el-table-column label="操作" width="120" align="center">
            <!-- 使用自定义插槽自定义结构，row:当前行属性对象 -->
            <template #default="{ row }">
              <!-- 修改已有属性的按钮  type按钮类型 size按钮大小 icon按钮图标-->
              <el-button
                type="warning"
                size="small"
                icon="Edit"
                @click="updateAttr(row)"
                v-has="`btn.Attr.update`"
              ></el-button>
              <!-- 删除按钮气泡提示框 title 提示框标题 confirm点击确认按钮的回调-->
              <el-popconfirm
                width="200"
                confirm-button-text="确定"
                cancel-button-text="取消"
                icon="Warning"
                :title="`确定要删除${row.attrName}吗?`"
                @confirm="deleteAttr(row)"
              >
                <template #reference>
                  <!-- 插槽展示删除按钮 -->
                  <el-button
                    type="danger"
                    size="small"
                    icon="Delete"
                    v-has="`btn.Attr.remove`"
                  ></el-button>
                </template>
              </el-popconfirm>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <!-- 添加/修改属性值列表结构 -->
      <div v-show="scene === 1">
        <!-- 行内表单 :inline="true" @submit.prevent阻止表单默认提交-->
        <el-form :inline="true" @submit.prevent>
          <el-form-item label="属性名称:">
            <el-input
              placeholder="请输入属性名称"
              clearable
              v-model="attrParams.attrName"
            ></el-input>
          </el-form-item>
        </el-form>
        <!-- 添加属性值按钮 -->
        <el-button
          type="primary"
          :disabled="!attrParams.attrName"
          icon="Plus"
          @click="addAttrValue"
        >
          添加属性值
        </el-button>
        <el-button @click="cancel">取消</el-button>
        <!-- 属性值展示/添加表格 -->
        <el-table
          :data="attrParams.attrValueList"
          border
          stripe
          style="width: 100%; margin: 10px 0"
        >
          <el-table-column
            type="index"
            label="序号"
            align="center"
            width="110"
          />
          <el-table-column label="属性值名称" align="center">
            <!-- 默认插槽获取到当前行的属性值对象和当前行属性值的索引 -->
            <template #default="{ row, $index }">
              <!-- .trim表单收集数据时去除两端空格 @blur失焦事件 ref获取组件实例对象 -->
              <el-input
                v-model.trim="row.valueName"
                placeholder="请输入属性值名称"
                v-show="row.flag"
                @blur="toLook(row, $index)"
                :ref="(vc:any)=>inputArr[$index] = vc"
              ></el-input>
              <!-- 用于编辑模式与查看模式的切换 -->
              <div v-show="!row.flag" @click="toEdit(row, $index)">
                {{ row.valueName }}
              </div>
            </template>
          </el-table-column>
          <el-table-column label="属性值操作" width="200">
            <template #default="{ row, $index }">
              <!-- 当处于修改属性值时，才会显示修改按钮 -->
              <el-button
                type="warning"
                size="small"
                icon="Edit"
                v-show="attrParams.id"
                @click="toEdit(row, $index)"
              ></el-button>
              <!-- 删除当前属性值 根据当前行数据的索引值进行删除 -->
              <el-button
                type="danger"
                size="small"
                icon="Delete"
                @click="attrParams.attrValueList.splice($index, 1)"
              ></el-button>
            </template>
          </el-table-column>
        </el-table>
        <!-- 保存按钮 -->
        <el-button
          type="primary"
          :disabled="!attrParams.attrValueList.length"
          @click="save"
        >
          保存
        </el-button>
        <!-- 取消按钮 -->
        <el-button @click="cancel">取消</el-button>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts" name="Attr">
import { reqAddOrUpdate, reqAttr, reqDeleteAttr } from '@/api/product/attr'
import type { AttrObj, AttrValue } from '@/api/product/attr/type'
import { ElMessage, ElMessageBox } from 'element-plus'
import { nextTick, reactive, ref } from 'vue'

// 存储Category组件传递的数据
let c1Id = ref<number>()
let c2Id = ref<number>()
let c3Id = ref<number>()
// 存储属性对象列表数据
const attrList = ref<AttrObj[]>([])
// 定义一个数组：用于存储input组件对应的实例对象
const inputArr = ref<any>([])
// 定义控制卡片组件添加/修改与展示页面的切换
let scene = ref(0) // 0:展示页面，1:展示添加与修改结构
// 收集新增的属性对象的值
const attrParams = reactive<AttrObj>({
  // id: undefined, // 已有属性的id 携带id为修改不携带为新增一个属性
  attrName: '', // 已有的属性的名字
  categoryId: -1, // 当前属性属于哪个三级分类
  categoryLevel: 3, // 三级分类
  attrValueList: [], // 属性值列表
})

// 自定义事件用于获取Category组件中选中的三级分类id
const getID = async (c1id: number, c2id: number, c3id: number) => {
  // console.log(c1id, c2id, c3id)
  if (c3id) {
    // 当三级分类id不为空时，获取分类id数据并发送请求获取属性列表数据
    c1Id.value = c1id
    c2Id.value = c2id
    c3Id.value = c3id
    // 获取属性列表
    await getAttr()
  } else {
    // 当传递过来的数据中三级分类id为空时，将属性列表清空
    attrList.value = []
  }
}
// 获取已有的属性对象方法
const getAttr = async () => {
  // 清空属性列表
  attrList.value = []
  // 发请求获取属性列表
  const result = await reqAttr(
    c1Id.value as number,
    c2Id.value as number,
    c3Id.value as number,
  )
  if (result.code == 200) {
    attrList.value = result.data
  }
}
// 添加属性按钮的回调
const addAttr = () => {
  // 切换为添加与修改属性页面
  scene.value = 1
  // 清空上一次收集到的数据
  attrParams.id = undefined
  attrParams.attrName = ''
  attrParams.attrValueList = []
  // 收集当前三级分类id
  attrParams.categoryId = c3Id.value as number
}
// 修改属性按钮的回调
const updateAttr = (row: AttrObj) => {
  // 将要编辑的属性的全部数据合并到attrParams中 实现清空+赋值操作
  Object.assign(attrParams, JSON.parse(JSON.stringify(row))) // id,attrName,attrValueList
  // 此处必须使用深拷贝否则对属性修改后不点击保存数据也会发生变化,解决办法JSON.parse(JSON.stringify(row))
  // 切换为添加与修改属性页面
  scene.value = 1
}
// 删除属性按钮的回调
const deleteAttr = async (row: AttrObj) => {
  // 发请求删除指定id的属性对象
  const result = await reqDeleteAttr(row.id as number)
  if (result.code === 200) {
    ElMessage.success('删除成功')
    // 重新获取属性列表的数据
    getAttr()
  } else {
    ElMessage.error('删除失败')
  }
}
// 添加属性值按钮的回调
const addAttrValue = () => {
  // 向属性值列表中添加一个空的属性值对象
  attrParams.attrValueList.push({
    valueName: '',
    flag: true,
  })
  // 等待页面的input渲染完毕后去自动聚焦最后一个input
  nextTick(() => {
    // 此处不使用inputArr的下标是因为其内部存在值为null并未清除对应的元素
    inputArr.value[attrParams.attrValueList.length - 1].focus()
  })
}
// 属性值表单元素失去焦点的事件回调
const toLook = (row: AttrValue, $index: number) => {
  // 当属性值为空时不能变为div，弹窗提示并删除该元素
  if (row.valueName === '') {
    ElMessageBox.alert('属性值不能为空', '提示', {
      type: 'warning',
      confirmButtonText: 'OK',
    })
    // 删除属性值为空的元素
    attrParams.attrValueList.splice($index, 1)
    return
  }
  // 当属性值已经存在时，不能进行添加
  let repeat = attrParams.attrValueList.find((item) => {
    // 返回匹配到的重复对象
    // 遍历匹配时要将当前元素在列表中去除
    if (item != row) {
      return item.valueName === row.valueName
    }
  })
  if (repeat) {
    ElMessage({
      type: 'warning',
      message: '属性值已存在',
    })
    // 将重复的属性值删除
    attrParams.attrValueList.splice($index, 1)
    return
  }
  // 将flag变为false展示div
  row.flag = false
}
// 属性值div点击事件
const toEdit = (row: AttrValue, $index: number) => {
  // 将flag变为true展示input
  row.flag = true
  // 等模板渲染完毕，在将input聚焦
  nextTick(() => {
    inputArr.value[$index].focus()
  })
}
// 保存按钮的回调
const save = async () => {
  // 向服务器发送请求
  const result = await reqAddOrUpdate(attrParams)
  if (result.code == 200) {
    ElMessage({
      type: 'success',
      // 上传的参数有id为修改,无id为添加
      message: attrParams.id ? '修改成功' : '添加成功',
    })
    // 回到属性展示页面
    scene.value = 0
    // 并重新获取属性数据
    await getAttr()
  } else {
    ElMessage({
      type: 'error',
      message: attrParams.id ? '修改失败' : '添加失败',
    })
  }
}
// 取消按钮的回调
const cancel = () => {
  // 返回展示页面
  scene.value = 0
}
</script>

<style scoped></style>
